#include <gtest/gtest.h>  //注意斜杠方向，左斜杠有可能找不到头文件

#include <iostream>

using namespace std;

int Foo(int a, int b)
{
    if (0 == a || 0 == b)
        throw "don't do that";
    int c = a % b;
    if (0 == c)
    {
        return b;
    }
    return Foo(b, c);
}

TEST(FooTest, HandleNoneZeroInput)  //名称随便
{
    EXPECT_EQ(2, Foo(4, 10));
    EXPECT_EQ(6, Foo(30, 18));
}

/*
全局事件：需要创建一个自己的类，然后继承testing::Environment类，
分别实现成员函数SetUp()和TearDown()，同时在main函数内进行调用，
即"testing::AddGlobalTestEnvironment(new MyEnvironment);"，
通过调用函数我们可以添加多个全局的事件机制。需要注意的是，
全局事件在--gtest_repeat参数大于1的情况下（可以通过--help查看其他参数），会执行多次
*/
class MyEnvironment0 : public testing::Environment
{
   public:
    virtual void SetUp()
    {
        cout << "Global event0 : start1" << endl;
    }

    virtual void TearDown()
    {
        cout << "Global event0 : end" << endl;
    }
};

class MyEnvironment1 : public testing::Environment
{
   public:
    virtual void SetUp()
    {
        cout << "Global event1 : start" << endl;
    }

    virtual void TearDown()
    {
        cout << "Global event1 : end" << endl;
    }
};

TEST(GlobalTest0, test0)
{
    EXPECT_EQ(1, 1);
};

TEST(GlobalTest0, test1)
{
    EXPECT_EQ(1, 1);
};

TEST(GlobalTest1, test0)
{
    EXPECT_EQ(1, 1);
};

/*
套件事件：继承testing::Test，实现两个静态函数SetUpTestCase()和TearDownTestCase()，测试套件的事件机制不需要像全局事件机制一样在main注册，而是需要将我们平时使用的TEST宏改为TEST_F宏
*/
class MyTestSuite0 : public testing::Test
{
   protected:
    // 网上大部分写的是SetUpTestSuite，google后面升级版本了，改成SetUpTestCase
    // 相关讨论：https://stackoverflow.com/questions/54468799/google-test-using-setuptestsuite-doesnt-seem-to-work
    static void SetUpTestCase()
    {
        cout << "TestSuite event0 : start" << endl;
    }

    static void TearDownTestCase()
    {
        cout << "TestSuite event0 : end" << endl;
    }
};

class MyTestSuite1 : public testing::Test
{
   protected:
    static void SetUpTestCase()
    {
        cout << "TestSuite event1 : start" << endl;
    }

    static void TearDownTestCase()
    {
        cout << "TestSuite event1 : end" << endl;
    }
};

// 必须用TEST实现，实现上是拼成一个类MyTestSuite0_test0
TEST_F(MyTestSuite0, test0)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestSuite1, test0)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestSuite0, test1)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestSuite1, test1)
{
    EXPECT_EQ(1, 1);
}

/*
用例事件：测试用例的事件机制的创建和测试套件的基本一样，不同地方在于测试用例实现的两个函数分别是SetUp()和TearDown(),这两个函数不是静态函数了。SetUp()函数是在一个测试用例的开始前执行。TearDown()函数是在一个测试用例的结束后执行。
*/
class MyTestCase0 : public testing::Test
{
   protected:
    virtual void SetUp()
    {
        cout << "TestCase event0 : start" << endl;
    }

    virtual void TearDown()
    {
        cout << "TestCase event0 : end" << endl;
    }
};

class MyTestCase1 : public testing::Test
{
   protected:
    virtual void SetUp()
    {
        cout << "TestCase event1 : start" << endl;
    }
    virtual void TearDown()
    {
        cout << "TestCase event1 : end" << endl;
    }
};

TEST_F(MyTestCase0, test0)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestCase0, test1)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestCase1, test0)
{
    EXPECT_EQ(1, 1);
}

TEST_F(MyTestCase1, test1)
{
    EXPECT_EQ(1, 1);
}

/* ---------------------------------------------------------------- */
struct LinkNode
{
    int _data;
    LinkNode *_next;
    LinkNode(const int &data) : _data(data), _next(NULL) {}
};

class Link
{
   public:
    Link() : pHead(new LinkNode(0)) {}
    void PushBack(const int &data)
    {
        if (pHead == NULL)
            return;
        LinkNode *newNode = new LinkNode(data);
        if (pHead->_next == NULL)
        {  //第一次插入结点
            pHead->_next = newNode;
        }
        else
        {  //找到最后一个结点直接尾插
            LinkNode *cur = pHead->_next;
            while (cur->_next)
            {
                cur = cur->_next;
            }
            cur->_next = newNode;
        }
    }

    void PopBack()
    {
        if (pHead == NULL)
            return;
        LinkNode *cur = pHead;
        LinkNode *prev = NULL;
        while (cur->_next)
        {
            prev = cur;
            cur = cur->_next;
        }
        prev->_next = NULL;
        delete cur;
    }

    LinkNode *FindNode(const int &data)
    {
        if (pHead == NULL)
            return NULL;
        LinkNode *cur = pHead->_next;
        while (cur)
        {
            if (cur->_data == data)
                return cur;
            cur = cur->_next;
        }
        return NULL;
    }

    bool Delete(int data)
    {
        LinkNode *pos = FindNode(data);
        if (pos == NULL)
            return false;
        LinkNode *cur = pHead->_next;
        while (cur->_next != pos)
        {
            cur = cur->_next;
        }
        cur->_next = pos->_next;
        delete pos;
        return true;
    }

    void Destroy()
    {
        if (pHead == NULL)
            return;
        LinkNode *cur = pHead->_next;
        while (cur)
        {
            LinkNode *del = cur;
            cur = cur->_next;
            delete del;
            del = NULL;
        }
        delete pHead;  //删除头结点
    }
    LinkNode *pHead;
};

class TestLink : public testing::Test
{
   public:
    virtual void SetUp()
    {
        cout << "SetUp" << endl;
        for (int i = 1; i <= 5; i++)
        {
            link.PushBack(i);
        }
    }
    virtual void TearDown()
    {
        cout << "TearDown" << endl;
        link.Destroy();
    }
    Link link;
};

TEST_F(TestLink, PushBack)
{
    ASSERT_FALSE(link.pHead == NULL);
    link.PushBack(9);
    LinkNode *res = link.FindNode(9);
    ASSERT_FALSE(res == NULL);
}

TEST_F(TestLink, PopBack)
{
    for (int i = 1; i <= 5; i++)
    {
        link.PopBack();
    }
}

TEST_F(TestLink, FindNode)
{
    ASSERT_TRUE(link.FindNode(3));
    ASSERT_TRUE(link.FindNode(2));
    ASSERT_TRUE(link.FindNode(4));
    ASSERT_TRUE(link.FindNode(5));
    ASSERT_TRUE(link.FindNode(1));
    ASSERT_FALSE(link.FindNode(7));
}

TEST_F(TestLink, Delete)
{
    ASSERT_FALSE(link.pHead == NULL);
    ASSERT_TRUE(link.Delete(3) == true);
    ASSERT_TRUE(link.Delete(9) == false);
}

int main(int argc, char *argv[])
{
    testing::AddGlobalTestEnvironment(new MyEnvironment0);
    testing::AddGlobalTestEnvironment(new MyEnvironment1);

    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}